#include "./cmd_uart.h"
#include "../device/gpio.h"
#include "../device/dma.h"
#include "../rtos/event_flags.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>

namespace driver {

using namespace device;

using TX = PB<10>;
using RX = PB<11>;

using TXDMA = Dma<1, 3>;
static constexpr uint8_t TXChan = 4;
#define TX_STREAM DMA1_Stream3

using RXDMA = Dma<1, 1>;
static constexpr uint8_t RXChan = 4;
#define RX_STREAM DMA1_Stream1

CmdUart::CmdUart() {

    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN + RCC_AHB1ENR_DMA1EN;
    RCC->APB1ENR |= RCC_APB1ENR_USART3EN;

    ConfigAf<7, TX, RX>();
	USART3->CR1 = 0x00;
	USART3->CR2 = 0x00;
	USART3->CR3 = 0xC0;
	USART3->BRR = 42000000 / 115200;
	USART3->CR1 = 0x201C;

    RXDMA::ClrFlag();
    RX_STREAM->CR = 0x00;
    RX_STREAM->M0AR = reinterpret_cast<uint32_t>(m_recv_buf);
    RX_STREAM->PAR = reinterpret_cast<uint32_t>(&USART3->DR);
    RX_STREAM->NDTR = RecvBuffSize;
    RX_STREAM->CR = (RXChan << 25) +
        (0x01 << 10) +
        (0x01 << 8) +
        (0x01);

    rtos::flags::global->set(rtos::flags::CmdUartTxFlag);
    NVIC_EnableIRQ(DMA1_Stream3_IRQn);
    NVIC_EnableIRQ(USART3_IRQn);
}

void CmdUart::write_by_dma(const void *buf, uint32_t len) {
    TXDMA::ClrFlag();
    TX_STREAM->CR = 0x00;
    TX_STREAM->M0AR = reinterpret_cast<uint32_t>(buf);
    TX_STREAM->PAR = reinterpret_cast<uint32_t>(&USART3->DR);
    TX_STREAM->NDTR = len;
    TX_STREAM->CR = (TXChan << 25) +
        (0x01 << 10) +
        (0x01 << 6) +
        (0x01 << 4) +
        (0x01);
}

void CmdUart::throw_log(uint8_t ec, const char *fmt, ...) {
    using namespace utils;
    
    va_list ap;
    va_start(ap, fmt);

    wait_write_complete();

    encode_uint16(m_write_buf, proto::Head);
    //encode_uint16(m_write_buf + 2, 0)
    encode_uint16(m_write_buf + 4, proto::Except | proto::SalveFlag | proto::NotifyFlag);
    encode_uint8(m_write_buf + 6, ec);
    int n = vsprintf( reinterpret_cast<char *>(m_write_buf + 9), fmt, ap);
    encode_uint16(m_write_buf + 7, n);
    encode_uint16(m_write_buf + 2, n + 8 + 2 + 1);

    uint8_t s = alg::sun_xor(m_write_buf + 6, n + 2 + 1);
    encode_uint8(m_write_buf + n + 2 + 1 + 6, s);
    encode_uint8(m_write_buf + n + 2 + 1 + 7, proto::End);

    write_by_dma(m_write_buf, n + 8 + 2 + 1);
}

void CmdUart::wait_write_complete() {
    rtos::flags::global->wait_any(rtos::flags::CmdUartTxFlag);
}

void CmdUart::on_recv_idle() {
    uint32_t index = RecvBuffSize - RX_STREAM->NDTR;
    if (index == m_recv_index) {
        return;
    }
    utils::byte_view_t bw;
    if (index > m_recv_index) {
        bw.buf = m_recv_buf + m_recv_index;
        bw.len = index - m_recv_index;
    } else {
        uint32_t k = RecvBuffSize - m_recv_index;
        memcpy(m_copy_buf, m_recv_buf + m_recv_index, k);
        memcpy(m_copy_buf + k, m_recv_buf, index);
        bw.buf = m_copy_buf;
        bw.len = k + index;
    }
    m_recv_index = index;
    m_mq.post(bw);
}

}

using namespace driver;

extern "C"
void DMA1_Stream3_IRQHandler() {
    if (!TXDMA::IsTC()) {
        return;
    }
    TXDMA::ClrFlag();
    rtos::flags::global->set(rtos::flags::CmdUartTxFlag);
}

extern "C"
void USART3_IRQHandler() {
    if ((USART3->SR & USART_SR_IDLE) != 0) {
        uint32_t clr = USART3->DR;
        cmdUart->on_recv_idle();
    }
}





